package cn.com.bod.fas.common.aop;

import cn.com.bod.fas.common.model.message.WebLogMsgVo;
import cn.com.bod.fas.common.service.feign.FeignCommonAuditService;
import cn.com.bod.fas.common.utils.SequenceUtil;
import cn.com.bod.fas.common.utils.UserInfoUtil;
import cn.com.bod.fas.common.constant.enumconst.MsgTypeEnum;
import cn.com.bod.fas.common.constant.enumconst.SequenceCodeEnum;
import cn.com.bod.fas.common.model.vo.WebLogVo;
import cn.com.yusys.yusp.commons.dto.User;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


/**
 * @description http接口访问日志AOP.
 * @author chenyuanxian
 * @date 2019年9月16日 下午3:52:45
 * @version 1.0.0
 * @Copyright (c) scfhdt
 */

//@Aspect
//@Component
@Slf4j
public class WebLogAspect {

    @Autowired
    FeignCommonAuditService feignCommonAuditService;

    @Autowired
    private SequenceUtil sequenceUtil;

    @Autowired
    private UserInfoUtil userInfoUtil;

    
    ThreadLocal<WebLogVo> webLogVoLocal = new ThreadLocal<WebLogVo>();
    
    /**
     * @description 拦截所有Controller层.
     * @author chenyuanxian
     * @date 2019年9月16日 下午3:54:40
     */
    @Pointcut("within(cn.com.scfhdt..*.controller..*Controller)")
    public void webControllerPointcut() {
    	log.debug("拦截所有Controller层");
    }
    
    /**
     * @description 定义http接口访问日志全局切入点.
     * @author chenyuanxian
     * @date 2019年9月16日 下午3:55:38
     */
    @Pointcut("webControllerPointcut()")
    private void webPointcut() {
    	log.debug("定义http接口访问日志全局切入点");
    }
    
    /**
     * @description 接口请求日志.
     * @author chenyuanxian
     * @date 2019年9月16日 下午3:56:09
     * @param joinPoint
     */
    @Before("webPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            WebLogVo webLogVo = getWebLogInfo(request, joinPoint);
            webLogVo.setStartTime(System.currentTimeMillis());
            webLogVoLocal.set(webLogVo);
            //日志打印或推送到消息队列
			String msg = JSONObject.toJSONString(webLogVo);
            log.info("接口请求日志打印开始：{} 接口请求日志打印结束", msg);

            /* 创建消息 */
            WebLogMsgVo webLogMsgVo = createWebLogMsgVo();
            webLogMsgVo.setTitle("http接口请求日志消息");
            webLogMsgVo.setType(MsgTypeEnum.HTTP_REQUEST_LOG.getCode());
            webLogMsgVo.setContent(webLogVo);
            log.info("开始发送http接口请求日志到消息队列，ID={}", webLogMsgVo.getId());
            feignCommonAuditService.sendWebLogToRabbit(webLogMsgVo);
        } catch (Exception ex) {
            log.error("接口请求日志打印异常：{} ", ExceptionUtils.getStackTrace(ex));
        }
    }
    
    /**
     * @description 接口响应日志.
     * @author chenyuanxian
     * @date 2019年9月16日 下午10:53:03
     * @param joinPoint 切入点
     * @param retVal 返回值
     */
    @AfterReturning(pointcut = "webPointcut() target", returning = "retVal")
    public void doAfterReturning(JoinPoint joinPoint, Object retVal) {
        try {
            WebLogVo webLogVo = webLogVoLocal.get();
            long endTime = System.currentTimeMillis();
            webLogVo.setEndTime(endTime);
            webLogVo.setExecutionTime(endTime - webLogVo.getStartTime());
            webLogVo.setResponseParam(JSONObject.toJSONString(retVal));
            //日志打印或推送到消息队列
            log.info("接口响应日志打印开始：{} 接口响应日志打印结束", JSONObject.toJSONString(webLogVo));

            /* 创建消息 */
            WebLogMsgVo webLogMsgVo = createWebLogMsgVo();
            webLogMsgVo.setTitle("http接口响应日志消息");
            webLogMsgVo.setType(MsgTypeEnum.HTTP_RESPONSE_LOG.getCode());
            webLogMsgVo.setContent(webLogVo);
            log.info("开始发送http接口响应日志到消息对列，ID={}", webLogMsgVo.getId());
            feignCommonAuditService.sendWebLogToRabbit(webLogMsgVo);
        } catch (Exception ex) {
            log.error("接口响应日志打印异常：{} ", ExceptionUtils.getStackTrace(ex)); 
        }
    }
    
    /**
     * @description 接口异常日志打印.
     * @author chenyuanxian
     * @date 2019年9月16日 下午10:55:18
     * @param joinPoint 切入点
     * @param ex 异常
     */
    @AfterThrowing(pointcut = "webPointcut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        try {
            WebLogVo webLogVo = webLogVoLocal.get();
            long endTime = System.currentTimeMillis();
            webLogVo.setResponseParam(ExceptionUtils.getStackTrace(ex));
            webLogVo.setEndTime(endTime);
            webLogVo.setExecutionTime(endTime - webLogVo.getStartTime());
            log.error("接口异常日志打印开始：{} 接口异常日志打印结束", JSONObject.toJSONString(webLogVo));

            /* 创建消息 */
            WebLogMsgVo webLogMsgVo = createWebLogMsgVo();
            webLogMsgVo.setTitle("http接口响应日志消息");
            webLogMsgVo.setType(MsgTypeEnum.HTTP_EXCEPTION_LOG.getCode());
            webLogMsgVo.setContent(webLogVo);
            log.info("开始发送http接口响应日志到消息对列，ID={}", webLogMsgVo.getId());
            feignCommonAuditService.sendWebLogToRabbit(webLogMsgVo);
        } catch (Exception exception) {
            log.error("接口异常日志打印异常：{} ", ExceptionUtils.getStackTrace(exception)); 
        }
    }

    /**
     * @description 获取日志信息 
     * @author chenyuanxian
     * @date 2019年9月16日 下午10:59:37
     * @param request 请求对象
     * @param joinPoint 切入点
     * @return 日志信息
     */
    private WebLogVo getWebLogInfo(HttpServletRequest request, JoinPoint joinPoint) {
        WebLogVo webLogVo = new WebLogVo();
        webLogVo.setWebLogId(sequenceUtil.getTempSeqByCode(SequenceCodeEnum.WEB_LOG_ID));
        webLogVo.setRequestUrl(request.getRequestURL().toString());
        webLogVo.setRequestUri(request.getRequestURI());
        webLogVo.setRequestMethod(request.getMethod());
        webLogVo.setQueryString(request.getQueryString());
        webLogVo.setParameters(request.getParameterMap());
        webLogVo.setRemoteAddr(request.getRemoteAddr());
        webLogVo.setRemoteHost(request.getRemoteHost());
        webLogVo.setRemotePort(request.getRemotePort());
        webLogVo.setLocalAddr(request.getLocalAddr());
        webLogVo.setLocalName(request.getLocalName());
        webLogVo.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        webLogVo.setArgs(JSONObject.toJSONString(joinPoint.getArgs()));
        webLogVo.setHeaders(getHeadersInfo(request));
        return webLogVo;
    }
    
    /**
     * @description 获取请求头 
     * @author chenyuanxian
     * @date 2019年9月16日 下午10:22:15
     * @param request 请求对象
     * @return 请求头
     */
    private Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key =  headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    private WebLogMsgVo createWebLogMsgVo() {
        WebLogMsgVo webLogMsgVo = new WebLogMsgVo();
        webLogMsgVo.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        User user = userInfoUtil.getUserInfo();
        webLogMsgVo.setUserId(user.getUserId());
        webLogMsgVo.setUserName(user.getUserName());
        webLogMsgVo.setUserOrgId(user.getOrg().getId());
        webLogMsgVo.setUserOrgName(user.getOrg().getName());
        webLogMsgVo.setSendTime(System.currentTimeMillis());
        return webLogMsgVo;
    }
}
